package com.moon.interview.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.moon.interview.exception.SysErrorCode;
import com.moon.interview.constant.RedisConstant;
import com.moon.interview.exception.ApplicationException;
import com.moon.interview.mapper.UserMapper;
import com.moon.interview.model.dto.user.*;
import com.moon.interview.model.entity.User;
import com.moon.interview.model.vo.UserVo;
import com.moon.interview.service.IUserService;
import com.moon.interview.utils.DeviceUtils;
import com.moon.interview.utils.ThrowUtils;
import com.moon.interview.utils.UserUtils;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RBitSet;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;

import static com.moon.interview.constant.UserConstant.*;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Resource
    private RedissonClient redissonClient;

    /**
     * 校验
     * 根据传入的属性名称以及值进行校验
     * @param user
     * @param values 需要校验的属性名称
     */
    private void validateUser(User user, String... values) {
        if (values == null ||values.length == 0) return;
        for(String value : values) {
            switch (value) {
                case "userAccount": {
                    // 账号不能为空，且不能重复
                    if (StringUtils.isBlank(user.getUserAccount()) || user.getUserAccount().length() < 4) {
                        throw new ApplicationException(SysErrorCode.PARAMS_ERROR, "用户账号过短");
                    }
                    break;
                }
                case "userPassword": {
                    if (StringUtils.isBlank(user.getUserPassword()) || user.getUserPassword().length() < 8) {
                        throw new ApplicationException(SysErrorCode.PARAMS_ERROR, "用户密码过短");
                    }
                    break;
                }
            }
        }
    }

    /**
     * 判断用户账号是否存在
     * @param account  用户账号
     * @return
     */
    private Boolean isExistAccount(String account) {
        // 判断账号是否已存在
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUserAccount, account);
        List<User> users = this.baseMapper.selectList(queryWrapper);
        if (users != null && users.size() > 0) {
            return true;
        }
        return false;
    }

    /**
     * User转UserVo
     * @param user
     * @return userVo
     */
    public UserVo getUserVo(User user) {
        if (user == null) {
            return null;
        }
        UserVo userVo = new UserVo();
        BeanUtils.copyProperties(user, userVo);
        return userVo;
    }

    @Override
    public Long register(UserRegistryRequest userRegistryRequest) {
        // 获取参数
        String userAccount = userRegistryRequest.getUserAccount();
        String userPassword = userRegistryRequest.getUserPassword();
        String checkPassword = userRegistryRequest.getCheckPassword();
        // 创建user对象
        User user = new User();
        user.setUserAccount(userAccount);
        user.setUserPassword(userPassword);
        // 校验
        validateUser(user, "userAccount","userPassword");
        // 密码和校验密码相同
        if (!userPassword.equals(checkPassword)) {
            throw new ApplicationException(SysErrorCode.PARAMS_ERROR, "两次输入的密码不一致");
        }
        // 判断账户是否已存在
        Boolean exist = isExistAccount(userAccount);
        if (exist) {
            throw new ApplicationException(SysErrorCode.NOT_FOUND_ERROR, "账号已存在");
        }
        // 密码加密
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
        user.setUserPassword(encryptPassword);
        // 默认用户昵称就是用户账号
        user.setUserName(userAccount);
        // 默认用户角色是user
        user.setUserRole("user");
        boolean flag = this.save(user);
        if (!flag) {
            throw new ApplicationException(SysErrorCode.SYSTEM_ERROR,"注册失败，数据库错误");
        }
        return user.getId();
    }

    @Override
    public UserVo login(UserLoginRequest userLoginRequest, HttpServletRequest request) {
        String userAccount = userLoginRequest.getUserAccount();
        String userPassword = userLoginRequest.getUserPassword();
        User user = new User();
        user.setUserAccount(userAccount);
        user.setUserPassword(userPassword);
        // 参数校验
        validateUser(user,"userAccount","userPassword");
        // 密码加密
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
        // 验证账号
        User userInfo = this.getBaseMapper().selectOne(new LambdaQueryWrapper<User>()
                .eq(User::getUserAccount, userAccount)
                .eq(User::getUserPassword, encryptPassword));
        if (userInfo == null) {
            throw new ApplicationException(SysErrorCode.EXISTS, "账号或密码错误");
        }
        // 记录用户登录态
//        request.getSession().setAttribute(USER_LOGIN_STATE, userInfo);
        // sa-token 登录，并指定设备
        StpUtil.login(userInfo.getId(), DeviceUtils.getRequestDevice(request));
        StpUtil.getSession().set(USER_LOGIN_STATE, DeviceUtils.getRequestDevice(request));
        return this.getUserVo(userInfo);
    }

    /**
     * 退出登录
     * @return
     */
    @Override
    public Boolean logout() {
        StpUtil.checkLogin();
        // 移除登录态
        StpUtil.logout();
        return true;
    }


    @Override
    public Page<User> page(UserQueryRequest request) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StringUtils.isNotBlank(request.getUserRole()),User::getUserRole, request.getUserRole());  // 用户角色
        wrapper.like(StringUtils.isNotBlank(request.getUserName()), User::getUserName, request.getUserName());  // 昵称
        wrapper.like(StringUtils.isNotBlank(request.getUserProfile()), User::getUserProfile, request.getUserProfile());  // 用户简介
        wrapper.last(request.getOrderSql()); // 排序

        Page<User> page = this.page(new Page<>(request.getPageNum(), request.getPageSize()), wrapper);
        return page;
    }

    /**
     * 获取用户登录信息
     * @return
     */
    @Override
    public UserVo getUserInfo() {
        User user = UserUtils.getLoginUser();
        UserVo userVo = this.getUserVo(user);
        return userVo;
    }

    @Override
    public Boolean updatePassword(UserPasswordUpdateRequest updateRequest) {
        Long id = updateRequest.getId();
        String oldPassword = updateRequest.getOldPassword();
        String newPassword = updateRequest.getNewPassword();
        // 判断是否是同一用户操作
        User user = UserUtils.getLoginUser();
        ThrowUtils.throwIf(!user.getId().equals(updateRequest.getId()), SysErrorCode.OPERATION_ERROR, "不是同一用户操作");
        // 判断两次输入的密码是否相同
        ThrowUtils.throwIf(!newPassword.equals(updateRequest.getCheckPassword()), SysErrorCode.PARAMS_ERROR, "两次输入的密码不一致");
        // 对比密码是否相同
        User userInfo = this.getById(id);
        String encryptOldPassword = DigestUtils.md5DigestAsHex((SALT + oldPassword).getBytes());
        ThrowUtils.throwIf(!encryptOldPassword.equals(userInfo.getUserPassword()), SysErrorCode.PARAMS_ERROR, "旧密码错误");
        // 修改密码
        String encryptNewPassword = DigestUtils.md5DigestAsHex((SALT + newPassword).getBytes());
        userInfo.setUserPassword(encryptNewPassword);
        boolean flag = this.updateById(userInfo);
        ThrowUtils.throwIf(!flag, SysErrorCode.OPERATION_ERROR, "修改失败");
        return true;
    }

    @Override
    public Boolean addSign() {
        // 获取当前日期
        LocalDate now = LocalDate.now();
        String key = RedisConstant.getUserSignInRedisKey(now.getYear(), UserUtils.getLoginUser().getId());
        RBitSet bitSet = redissonClient.getBitSet(key);
        // 获取当前日期是当年的第几天
        int dayOfYear = now.getDayOfYear();
        if (!bitSet.get(dayOfYear)) {
            bitSet.set(dayOfYear, true);
        }
        return true;
    }

    @Override
    public List<Integer> getSignInfo(int year) {
        String key = RedisConstant.getUserSignInRedisKey(year, UserUtils.getLoginUser().getId());
        RBitSet bitSet = redissonClient.getBitSet(key);
        // 加载bitset到内存中，避免多次调用
        BitSet bitData = bitSet.asBitSet();
        // 统计签到的日期
        List<Integer> dayList = new ArrayList<>();
        // 从索引 0 开始查找下一个被设置为 1 的位
        int index = bitData.nextSetBit(0);
        while (index >= 0) {
            dayList.add(index);
            // 查找下一个被设置为 1 的位
            index = bitData.nextSetBit(index + 1);
        }
        return dayList;
    }

    /**
     * 新增用户
     * @param request
     * @return
     */
    @Override
    public Long addUser(UserAddRequest request) {
        if (request == null) {
            throw new ApplicationException(SysErrorCode.PARAMS_ERROR);
        }
        User user = JSONObject.parseObject(JSON.toJSONString(request), User.class);
        // 数据校验
        validateUser(user,"userAccount");
        // 判断账号是否重复
        Boolean exist = isExistAccount(user.getUserAccount());
        if (exist) {
            throw new ApplicationException(SysErrorCode.NOT_FOUND_ERROR, "账号已存在");
        }
        // 密码加密 默认密码 12345678
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + DEFAULT_PASSWORD).getBytes());
        user.setUserPassword(encryptPassword);
        // 设置默认参数
        user.setUserName(user.getUserAccount());
        user.setUserRole("user");
        boolean flag = this.save(user);
        if (!flag) {
            log.error(user.getUserAccount() + "  :存入数据表失败");
            throw new ApplicationException(SysErrorCode.SYSTEM_ERROR,"添加失败");
        }
        return user.getId();
    }

    @Override
    public Boolean removeUser(Long id) {
        boolean flag = this.removeById(id);
        if (!flag) {
            throw new ApplicationException(SysErrorCode.SYSTEM_ERROR,"删除失败");
        }
        return true;
    }

    @Override
    public Boolean updateUser(UserUpdateRequest updateRequest) {
        User user = new User();
        BeanUtils.copyProperties(updateRequest, user);
        // todo 暂时不允许更改账号
        user.setUserAccount(null);
        boolean flag = this.updateById(user);
        if (!flag) {
            throw new ApplicationException(SysErrorCode.SYSTEM_ERROR,"更新失败");
        }
        return true;
    }
}
